/*
 * Expression.cpp
 *
 *  Created on: 25.03.2014
 *      Author: Nicolas Winkler
 */

#include "Expression.h"
#include "OperationExpression.h"
#include "BigInteger.h"
#include <iostream>
#include <sstream>
#include <climits>

using namespace solver;


Expression::Expression(void)
{
}


Expression::~Expression(void)
{
}


const Expression* Expression::add(const Expression* b) const
{
	return new AdditionExpression(this, b);
}


const Expression* Expression::sub(const Expression* b) const
{
	return new SubtractionExpression(this, b);
}


const Expression* Expression::mult(const Expression* b) const
{
	return new MultiplicationExpression(this, b);
}


const Expression* Expression::div(const Expression* b) const
{
	return new DivisionExpression(this, b);
}


const Expression* Expression::pow(const Expression* b) const
{
	return new PowerExpression(this, b);
}


IntegerExpression::IntegerExpression(const math::BigInteger& mn)
{
	this->value = mn;
}


IntegerExpression::~IntegerExpression(void)
{
}


const Expression* IntegerExpression::evaluate(void) const
{
	return this;
}


std::string IntegerExpression::getAsString(void) const
{
	//std::stringstream str;
	//str << std::fixed  << value;
	//return str.str();
	return value.getAsString();
}


std::string IntegerExpression::toString(void) const
{
	std::stringstream str;
	str << "Constant[" << value.getAsString() << "]";
	return str.str();
}


const Expression* IntegerExpression::add(const Expression* b) const
{
	const IntegerExpression* intExpr =
			dynamic_cast<const IntegerExpression*> (b);

	if (intExpr != 0) {
		return new IntegerExpression(this->value + intExpr->value);
	}

	const DivisionExpression* divExpr =
				dynamic_cast<const DivisionExpression*> (b);
	if (divExpr != 0) {
		return divExpr->add(this);
	}
	return Expression::add(b);
}


const Expression* IntegerExpression::sub(const Expression* b) const
{
	const IntegerExpression* intExpr =
			dynamic_cast<const IntegerExpression*> (b);
	if (intExpr != 0) {
		return new IntegerExpression(this->value - intExpr->value);
	}

	const DivisionExpression* divExpr =
			dynamic_cast<const DivisionExpression*> (b);

	if (divExpr != 0) {
		const Expression* newNumerator = this->mult(divExpr->b)->sub(divExpr->a);
		DivisionExpression* newDivExpr = new DivisionExpression(newNumerator, divExpr->b);
		const Expression* expr = newDivExpr->evaluate();
		delete newDivExpr;
		return expr;
	}
	return Expression::sub(b);
}


const Expression* IntegerExpression::mult(const Expression* b) const
{
	const IntegerExpression* intExpr =
			dynamic_cast<const IntegerExpression*> (b);

	if (intExpr != 0) {
		return new IntegerExpression(this->value * intExpr->value);
	}

	const DivisionExpression* divExpr =
			dynamic_cast<const DivisionExpression*> (b);
	if (divExpr != 0) {
		return divExpr->mult(this);
	}
	return Expression::mult(b);
}


const Expression* IntegerExpression::div(const Expression* b) const
{
	using math::BigInteger;
	const IntegerExpression* intExpr =
			dynamic_cast<const IntegerExpression*> (b);

	if (intExpr != 0) {
		if (this->value % intExpr->value == 0)
				return new IntegerExpression(this->value / intExpr->value);
			else {
				if (BigInteger::ggt(this->value, intExpr->value) != 1) {
					BigInteger aInt = this->value;
					BigInteger bInt = intExpr->value;
					BigInteger::cancel(aInt, bInt);
					return new DivisionExpression(
							new IntegerExpression(aInt),
							new IntegerExpression(bInt)
							);
				}
			}
	}

	const DivisionExpression* divExpr = dynamic_cast<const DivisionExpression*> (b);
	if (divExpr != 0) {
		const Expression* numerator = this->mult(divExpr->b);
		const Expression* denominator = divExpr->a;
		DivisionExpression* div = new DivisionExpression(numerator, denominator);
		const Expression* ret = div->evaluate();
		delete div;
		return ret;
	}

	return Expression::div(b);
}


const Expression* IntegerExpression::pow(const Expression* b) const
{
	using math::BigInteger;
	const IntegerExpression* intExpr =
			dynamic_cast<const IntegerExpression*> (b);

	if (intExpr != 0) {
		return new IntegerExpression(BigInteger::pow(this->value, intExpr->value));
	}

	const DivisionExpression* divExpr =
			dynamic_cast<const DivisionExpression*> (b);
	if (divExpr != 0) {
		const IntegerExpression* intNumerator =
				dynamic_cast<const IntegerExpression*> (divExpr->a);
		const IntegerExpression* intDenominator =
				dynamic_cast<const IntegerExpression*> (divExpr->b);
		if (intNumerator == 0 || intNumerator->value != 1 || intDenominator == 0) {
			PowerExpression* powerExpr =
					new PowerExpression(
							this->pow(divExpr->a),
							new DivisionExpression(
									new IntegerExpression(1),
									divExpr->b
							)
					);
			const Expression* eval = powerExpr->evaluate();
			delete powerExpr;
			return eval;
		} else {
			BigInteger bi = BigInteger::root(this->value, intDenominator->value);
			if (BigInteger::pow(bi, intDenominator->value) == this->value)
				return new IntegerExpression(bi);
		}
	}
	return new PowerExpression(this, b);
}
